#include "gb28181_record.h"
#include "../gb28181_service.h"
#include "../gb28181_utils.h"
#include "utils_log.h"
#include <sstream>
#include <regex>
#include <iomanip>
#include <algorithm>
#include <cctype>

namespace El {
namespace GB28181 {

void GB28181Record::ProcessRecordQuery(eXosip_event_t *event, eXosip_t *exosip_context,
                                     const SipServerConfig &config,
                                     RecordQueryCallback queryCallback)
{
    if (!event || !event->request || !exosip_context) {
        EL_ERROR("Invalid parameters in ProcessRecordQuery");
        return;
    }

    osip_message_t *request = event->request;
    EL_INFO("Processing RecordInfo query for device {}", config.device_id);

    // Extract XML content
    std::string xml_content;
    osip_body_t *body = nullptr;
    osip_message_get_body(request, 0, &body);
    if (body && body->body) {
        xml_content = std::string(body->body);
    }

    if (xml_content.empty()) {
        EL_ERROR("No XML content found in RecordInfo query");
        // Send empty response
        SendRecordQueryResponse(exosip_context, event, {}, "", config.device_id);
        return;
    }

    // Parse record query info
    RecordQueryInfo query_info;
    if (!ParseRecordQueryXML(xml_content, query_info)) {
        EL_ERROR("Failed to parse RecordInfo query XML");
        SendRecordQueryResponse(exosip_context, event, {}, "", config.device_id);
        return;
    }

    EL_INFO("Record Query: device={}, channel={}, start={}, end={}, type={}",
             query_info.device_id, query_info.channel_id, 
             query_info.start_time, query_info.end_time, 
             RecordTypeToString(query_info.type));

    // Validate query parameters
    if (!ValidateRecordQuery(query_info)) {
        EL_ERROR("Invalid record query parameters");
        SendRecordQueryResponse(exosip_context, event, {}, query_info.sn, config.device_id);
        return;
    }

    // Execute record query
    std::vector<RecordInfo> records;
    if (queryCallback) {
        try {
            records = queryCallback(query_info);
            EL_INFO("Record query returned {} records", records.size());
        } catch (const std::exception& e) {
            EL_ERROR("Exception in record query callback: {}", e.what());
            records.clear();
        }
    } else {
        EL_WARN("No record query callback registered");
    }

    // Send response
    SendRecordQueryResponse(exosip_context, event, records, query_info.sn, config.device_id);
}

void GB28181Record::SendRecordQueryResponse(eXosip_t *exosip_context, eXosip_event_t *event,
                                          const std::vector<RecordInfo> &records,
                                          const std::string &sn,
                                          const std::string &device_id)
{
    if (!exosip_context || !event) {
        EL_ERROR("Invalid parameters in SendRecordQueryResponse");
        return;
    }

    // Create response XML
    std::string response_xml = CreateRecordResponseXML(records, device_id, sn);

    // Build and send response
    osip_message_t *response = nullptr;
    eXosip_message_build_answer(exosip_context, event->tid, 200, &response);

    if (response) {
        osip_message_set_content_type(response, "Application/MANSCDP+xml");
        osip_message_set_body(response, response_xml.c_str(), response_xml.length());
        eXosip_message_send_answer(exosip_context, event->tid, 200, response);
        
        EL_INFO("Sent RecordInfo response with {} records", records.size());
    } else {
        EL_ERROR("Failed to build RecordInfo response");
    }
}

bool GB28181Record::ParseRecordQueryXML(const std::string &xml, RecordQueryInfo &query)
{
    // Extract basic fields
    query.device_id = ExtractXMLValue(xml, "DeviceID");
    query.sn = ExtractXMLValue(xml, "SN");
    query.start_time = ExtractXMLValue(xml, "StartTime");
    query.end_time = ExtractXMLValue(xml, "EndTime");
    
    // Extract record type
    std::string type_str = ExtractXMLValue(xml, "Type");
    if (!type_str.empty()) {
        query.type = StringToRecordType(type_str);
    }
    
    // Channel ID is optional
    query.channel_id = ExtractXMLValue(xml, "ChannelID");
    if (query.channel_id.empty()) {
        query.channel_id = query.device_id; // Default to device ID
    }
    
    // Validate required fields
    if (query.device_id.empty() || query.sn.empty() || 
        query.start_time.empty() || query.end_time.empty()) {
        EL_ERROR("Missing required fields in RecordInfo query");
        return false;
    }
    
    return true;
}

std::string GB28181Record::CreateRecordResponseXML(const std::vector<RecordInfo> &records,
                                                  const std::string &device_id,
                                                  const std::string &sn)
{
    std::ostringstream xml;
    
    xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        << "<Response>\n"
        << "<CmdType>RecordInfo</CmdType>\n"
        << "<SN>" << sn << "</SN>\n"
        << "<DeviceID>" << device_id << "</DeviceID>\n"
        << "<SumNum>" << records.size() << "</SumNum>\n";
    
    if (!records.empty()) {
        xml << "<RecordList>\n";
        
        for (const auto& record : records) {
            xml << CreateRecordItemXML(record);
        }
        
        xml << "</RecordList>\n";
    }
    
    xml << "</Response>\n";
    
    return xml.str();
}

std::string GB28181Record::RecordTypeToString(RecordType type)
{
    switch (type) {
        case RecordType::ALL: return "all";
        case RecordType::TIME: return "time";
        case RecordType::ALARM: return "alarm";
        case RecordType::MANUAL: return "manual";
        case RecordType::MOTION: return "motion";
        default: return "all";
    }
}

RecordType GB28181Record::StringToRecordType(const std::string &type_str)
{
    std::string lower_type = type_str;
    std::transform(lower_type.begin(), lower_type.end(), lower_type.begin(), ::tolower);
    
    if (lower_type == "all") return RecordType::ALL;
    if (lower_type == "time") return RecordType::TIME;
    if (lower_type == "alarm") return RecordType::ALARM;
    if (lower_type == "manual") return RecordType::MANUAL;
    if (lower_type == "motion") return RecordType::MOTION;
    
    return RecordType::ALL; // Default
}

bool GB28181Record::ValidateTimeFormat(const std::string &time_str)
{
    // Validate ISO 8601 format: YYYY-MM-DDTHH:MM:SS
    std::regex time_regex(R"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})");
    return std::regex_match(time_str, time_regex);
}

std::chrono::system_clock::time_point GB28181Record::ParseTimeString(const std::string &time_str)
{
    if (!ValidateTimeFormat(time_str)) {
        return std::chrono::system_clock::time_point{};
    }
    
    std::tm tm = {};
    std::istringstream ss(time_str);
    ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
    
    if (ss.fail()) {
        return std::chrono::system_clock::time_point{};
    }
    
    return std::chrono::system_clock::from_time_t(std::mktime(&tm));
}

std::string GB28181Record::FormatTimeString(const std::chrono::system_clock::time_point &time_point)
{
    std::time_t time_t = std::chrono::system_clock::to_time_t(time_point);
    std::tm* tm = std::localtime(&time_t);
    
    std::ostringstream oss;
    oss << std::put_time(tm, "%Y-%m-%dT%H:%M:%S");
    
    return oss.str();
}

std::string GB28181Record::ExtractXMLValue(const std::string &xml, const std::string &tag)
{
    std::string start_tag = "<" + tag + ">";
    std::string end_tag = "</" + tag + ">";
    
    size_t start_pos = xml.find(start_tag);
    if (start_pos == std::string::npos) {
        return "";
    }
    
    start_pos += start_tag.length();
    size_t end_pos = xml.find(end_tag, start_pos);
    if (end_pos == std::string::npos) {
        return "";
    }
    
    return xml.substr(start_pos, end_pos - start_pos);
}

bool GB28181Record::ValidateRecordQuery(const RecordQueryInfo &query)
{
    // Check device ID
    if (query.device_id.empty()) {
        EL_ERROR("Empty device ID in record query");
        return false;
    }
    
    // Check time format
    if (!ValidateTimeFormat(query.start_time) || !ValidateTimeFormat(query.end_time)) {
        EL_ERROR("Invalid time format in record query");
        return false;
    }
    
    // Check time range
    auto start_time = ParseTimeString(query.start_time);
    auto end_time = ParseTimeString(query.end_time);
    if (start_time >= end_time) {
        EL_ERROR("Invalid time range: start_time >= end_time");
        return false;
    }
    
    return true;
}

std::string GB28181Record::CreateRecordItemXML(const RecordInfo &record)
{
    std::ostringstream xml;
    
    xml << "<Item>\n"
        << "<DeviceID>" << record.device_id << "</DeviceID>\n"
        << "<Name>" << record.name << "</Name>\n"
        << "<FilePath>" << record.file_path << "</FilePath>\n"
        << "<Address>" << record.address << "</Address>\n"
        << "<StartTime>" << record.start_time << "</StartTime>\n"
        << "<EndTime>" << record.end_time << "</EndTime>\n"
        << "<Secrecy>" << (record.secrecy ? "1" : "0") << "</Secrecy>\n"
        << "<Type>" << RecordTypeToString(record.type) << "</Type>\n"
        << "<FileSize>" << record.file_size << "</FileSize>\n"
        << "</Item>\n";
    
    return xml.str();
}

std::string GB28181Record::FormatFileSize(uint64_t size)
{
    const char* units[] = {"B", "KB", "MB", "GB", "TB"};
    int unit_index = 0;
    double file_size = static_cast<double>(size);
    
    while (file_size >= 1024.0 && unit_index < 4) {
        file_size /= 1024.0;
        unit_index++;
    }
    
    std::ostringstream oss;
    oss << std::fixed << std::setprecision(2) << file_size << " " << units[unit_index];
    return oss.str();
}

} // namespace GB28181
} // namespace El